// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

// ************************************************************************* //
//                  avtArrayDecompose2DExpression.C                          //
// ************************************************************************* //

#include <avtArrayDecompose2DExpression.h>

#include <math.h>

#include <vtkCellData.h>
#include <vtkDataArray.h>
#include <vtkDataSet.h>
#include <vtkDoubleArray.h>
#include <vtkIntArray.h>
#include <vtkPointData.h>

#include <ExprToken.h>
#include <avtExprNode.h>

#include <DebugStream.h>
#include <ExpressionException.h>
#include <ImproperUseException.h>

#include <string>
#include <vector>

// ****************************************************************************
//  Method: avtArrayDecompose2DExpression constructor
//
//  Purpose:
//      Defines the constructor.  Note: this should not be inlined in the
//      header because it causes problems for certain compilers.
//
//  Programmer: Hank Childs
//  Creation:   July 21, 2005
//
//  Modifications:
//
//    Alister Maguire, Tue Sep 24 11:15:10 MST 2019
//    Initialized canApplyToDirectDatabaseQOT. 
//
// ****************************************************************************

avtArrayDecompose2DExpression::avtArrayDecompose2DExpression()
{
    issuedWarning = false;
    usePosition = false;
    canApplyToDirectDatabaseQOT = true;
    index[0] = 0;
    index[1] = 0;
    position[0] = 0;
    position[1] = 1;
}


// ****************************************************************************
//  Method: avtArrayDecompose2DExpression destructor
//
//  Purpose:
//      Defines the destructor.  Note: this should not be inlined in the header
//      because it causes problems for certain compilers.
//
//  Programmer: Hank Childs
//  Creation:   July 21, 2005
//
// ****************************************************************************

avtArrayDecompose2DExpression::~avtArrayDecompose2DExpression()
{
}


// ****************************************************************************
//  Method: avtArrayDecompose2DExpression::DeriveVariable
//
//  Purpose:
//      Creates an array.
//
//  Arguments:
//      inDS      The input dataset.
//
//  Returns:      The derived variable.  The calling class must free this
//                memory.
//
//  Programmer:   Hank Childs
//  Creation:     July 21, 2005
//
//  Modifications:
//
//    Hank Childs, Fri Jun  9 14:22:43 PDT 2006
//    Remove unused variable.
//
//    Gunther H. Weber, Mon Apr 13 13:01:02 PDT 2015
//    Copied over to avtArrayDecompose2D and adpated it to handle "2D" arrays
//    generated by the Chombo reader
//
// ****************************************************************************

vtkDataArray *
avtArrayDecompose2DExpression::DeriveVariable(vtkDataSet *in_ds, int currentDomainsIndex)
{
    if (activeVariable == NULL)
        EXCEPTION2(ExpressionException, outputVariableName, 
                   "Asked to decompose an array, but did "
                   "specify which variable to decompose");

    vtkDataArray *data = in_ds->GetPointData()->GetArray(activeVariable);
    if (data == NULL)
        data = in_ds->GetCellData()->GetArray(activeVariable);

    if (data == NULL)
        EXCEPTION2(ExpressionException, outputVariableName, 
                   "Unable to locate variable to decompose");

    double dvpar = 1.0;
    double dmu = 1.0;
    vtkDoubleArray *dx_arr = dynamic_cast<vtkDoubleArray*>(in_ds->GetFieldData()->GetArray("dx_array"));
    vtkIntArray *v_base_index_arr = dynamic_cast<vtkIntArray*>(in_ds->GetFieldData()->GetArray("v_base_index"));
    vtkIntArray *v_dims_arr = dynamic_cast<vtkIntArray*>(in_ds->GetFieldData()->GetArray("v_dims"));
    if (dx_arr)
    {
        dvpar = dx_arr->GetValue(0);
        dmu = dx_arr->GetValue(1);
    }

    int v_base_index[2] = { 0, 0 };
    int v_dims[2] = { 0, 0 };
    if (v_base_index_arr && v_dims_arr)
    {
        for (int i = 0; i < 2; ++i)
        {
            v_base_index[i] = v_base_index_arr->GetValue(i);
            v_dims[i] = v_dims_arr->GetValue(i);
        }
    }
    else
    {
        EXCEPTION1(ImproperUseException,
                "Internal error: Velocity base index and dimensions not set by database plugin.");
    }

    if (usePosition)
    {
        index[0] = position[0] / dvpar;
        index[1] = position[1] / dmu;
    }

    if (index[0] < v_base_index[0] || index[0] >= v_base_index[0] + v_dims[0] ||
        index[1] < v_base_index[1] || index[1] >= v_base_index[1] + v_dims[1])
    {
        EXCEPTION1(ImproperUseException,
                "Internal error: Velocity base index and dimensions not set by database plugin.");
    }

    vtkIdType idx = (index[0] - v_base_index[0])*v_dims[1] + index[1] - v_base_index[1];

    if (idx < 0 || idx >= data->GetNumberOfComponents())
        EXCEPTION2(ExpressionException, outputVariableName, 
                "Index into array is not valid.");

    vtkDataArray *rv = data->NewInstance();
    vtkIdType nvals = data->GetNumberOfTuples();
    rv->SetNumberOfTuples(nvals);
    for (vtkIdType i = 0 ; i < nvals ; ++i)
        rv->SetTuple1(i, data->GetComponent(i, idx));

    return rv;
}


// ****************************************************************************
//  Method: avtArrayDecompose2DExpression::ProcessArguments
//
//  Purpose:
//      Tells the first argument to go generate itself.  Parses the second
//      argument into a list of material names.
//
//  Arguments:
//      inDS      The input dataset.
//
//  Returns:      The derived variable.  The calling class must free this
//                memory.
//
//  Modifications:
//    Gunther H. Weber, Mon Apr 13 13:01:02 PDT 2015
//    Copied over to avtArrayDecompose2D and adpated it to handle "2D" arrays
//    generated by the Chombo reader
//
//  Programmer:   Hank Childs
//  Creation:     July 21, 2005
//
// ****************************************************************************

void
avtArrayDecompose2DExpression::ProcessArguments(ArgsExpr *args, 
        ExprPipelineState *state)
{
    // Check the number of arguments
    std::vector<ArgExpr*> *arguments = args->GetArgs();
    size_t nargs = arguments->size();
    if (nargs != 3)
    {
        EXCEPTION2(ExpressionException, outputVariableName,
                "this expression must be specified with exactly three "
                "arguments.  Usage: array_decompose(array, k-index, l-index)");
    }

    // Tell the first argument to create its filters.
    ArgExpr *firstarg = (*arguments)[0];
    avtExprNode *firstTree = dynamic_cast<avtExprNode*>(firstarg->GetExpr());
    firstTree->CreateFilters(state);

    ArgExpr *secondarg = (*arguments)[1];
    ExprParseTreeNode *secondTree = secondarg->GetExpr();
    std::string type = secondTree->GetTypeName();
    ArgExpr *thirdarg = (*arguments)[2];
    ExprParseTreeNode *thirdTree = thirdarg->GetExpr();
    if (type != thirdTree->GetTypeName())
    {
        EXCEPTION2(ExpressionException, outputVariableName, "Second and third argument"
               " to array_decompose must be of same type (integer or float constant).");
    }

    if (thirdTree->GetTypeName() == type)
    {
        if (type == "IntegerConst")
        {
            index[0] = dynamic_cast<IntegerConstExpr*>(secondTree)->GetValue();
            index[1] = dynamic_cast<IntegerConstExpr*>(thirdTree)->GetValue();
        }
        else if (type == "FloatConst")
        {
            position[0] = dynamic_cast<FloatConstExpr*>(secondTree)->GetValue();
            position[1] = dynamic_cast<FloatConstExpr*>(thirdTree)->GetValue();
            usePosition = true;
        }
        else
        {
            debug5 << "avtArrayDecompose2DExpression: Second argument is not an int or float."
                << endl;
            EXCEPTION2(ExpressionException, outputVariableName, "Second argument to array_decompose "
                    "must be a number.");
        }
    }
}


// ****************************************************************************
//  Method: avtArrayDecompose2DExpression::PreExecute
//
//  Purpose:
//      Called before execution.  This sets the issuedWarning flag to false.
//
//  Programmer: Hank Childs
//  Creation:   July 21, 2005
//
//  Modifications:
//    Jeremy Meredith, Thu Feb 15 12:02:51 EST 2007
//    Call inherited PreExecute before everything else.
//
// ****************************************************************************

    void
avtArrayDecompose2DExpression::PreExecute(void)
{
    avtSingleInputExpressionFilter::PreExecute();
    issuedWarning = false;
}


